﻿using System;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.ComponentModel;
using System.Drawing.Design;
using System.Reflection;
using System.ComponentModel.Design;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

namespace UtfprLibrary.Windows.Forms.ComponentModel {
    internal class UtfprQueryEditor : UITypeEditor {
        private ITypeDescriptorContext _context = null;
        private IServiceProvider _provider = null;
        private List<UtfprComponentDescription> _componentList = null;

        /// <summary>
        /// Displays an ellipsis (...) button to start a modal dialog box.
        /// </summary>
        /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
        public override UITypeEditorEditStyle GetEditStyle(
            ITypeDescriptorContext context) {
            return UITypeEditorEditStyle.Modal;
        }

        /// <summary>
        /// Edits the value of the specified object using the specified service provider and context.
        /// </summary>
        /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
        /// <param name="provider">A service provider object through which editing services can be obtained.</param>
        /// <param name="value">The object to edit the value of. </param>
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
            if (context != null && provider != null) {
                this._context = context;
                this._provider = provider;

                var dlgProgress = new UtfprFormProgressBase("Aquarde...", "Cancelando...", true, longOperation);
                if (dlgProgress.ShowDialog() == DialogResult.OK) {
                    var result = showEditorDialog(_componentList);
                    if (result.IsDialogResultOK) {
                        value = result.Value;
                    }
                }
            }
            return value;
        }

        /// <summary>
        /// No special thumbnail will be shown for the grid.
        /// </summary>
        /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
        public override bool GetPaintValueSupported(System.ComponentModel.ITypeDescriptorContext context) {
            return false;
        }

        #region Private Methods

        /// <summary>
        /// Show a UtfprFormPickerEditor dialog, retrieving the result value.
        /// </summary>
        private UtfprFormResult showEditorDialog(List<UtfprComponentDescription> componentList) {
            UtfprFormResult result = new UtfprFormResult();
            result.IsDialogResultOK = false;
            try {
                IWindowsFormsEditorService editorService = _provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
                if (editorService != null) {
                    UtfprFormPickerEditor utfprFormPickerEditor = new UtfprFormPickerEditor();
                    utfprFormPickerEditor.FillDataGrid(componentList);
                    if (editorService.ShowDialog(utfprFormPickerEditor) == DialogResult.OK) {
                        result.IsDialogResultOK = true;
                        result.Value = utfprFormPickerEditor.Value;
                    }
                }
            } catch { }
            return result;
        }

        /// <summary>
        /// Get the types that where utfprLookupFieldBindingSourceType and formQueryBindingSourceType match.
        /// </summary>
        private List<Type> getDesiredTypes(Type utfprLookupFieldBindingSourceType, List<Type> assemblyTypes) {
            var types = new List<Type>();
            try {
                if (utfprLookupFieldBindingSourceType != null) {
                    foreach (Type type in assemblyTypes) {
                        Type formQueryBindingSourceType = getFormQueryBindingSourceType(type);
                        if (formQueryBindingSourceType != null) {
                            if (utfprLookupFieldBindingSourceType.FullName == formQueryBindingSourceType.FullName) {
                                types.Add(type);
                            }
                        }
                    }
                }
            } catch { }
            return types;
        }

        /// <summary>
        /// Get the types defined in the RootComponent.
        /// </summary>
        private List<Type> getAssemblyTypes() {
            var types = new List<Type>();
            try {
                IDesignerHost host = (IDesignerHost)_provider.GetService(typeof(IDesignerHost));
                ITypeResolutionService resolution = (ITypeResolutionService)_provider.GetService(typeof(ITypeResolutionService));
                //http://stackoverflow.com/questions/2658275/c-sharp-assembly-gettypes-reflectiontypeloadexception
                AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
                    foreach (var assembly in ((AppDomain)sender).GetAssemblies()) {
                        if (assembly.FullName == args.Name) {
                            return assembly;
                        }
                    }
                    return null;
                };

                Type rootComponentType = resolution.GetType(host.RootComponentClassName, false);
                types = rootComponentType.Assembly.GetTypes().ToList();
            } catch { }

            return types;
        }

        /// <summary>
        /// Get the UtfprLookupFieldBase instance
        /// </summary>
        private UtfprLookupFieldBase getUtfprLookupField() {
            UtfprLookupFieldBase utfprLookupField = null;
            try {
                //smart-tag
                if (_context.Instance.GetType() == typeof(UtfprLookupFieldActionList)) {
                    var actionList = (UtfprLookupFieldActionList)_context.Instance;
                    var flags = BindingFlags.Instance | BindingFlags.NonPublic;
                    var control = actionList.GetType().GetField("LinkedControl", flags);
                    utfprLookupField = (UtfprLookupFieldBase)control.GetValue(actionList);
                    //properties-window
                } else {
                    utfprLookupField = (UtfprLookupFieldBase)_context.Instance;
                }
            } catch { }
            return utfprLookupField;
        }

        /// <summary>
        /// Get the DataSource type defined in UtfprLookupFieldBase. It expects that the DataSource is a 
        /// BindingSource, getting in reality the BindingSource.DataSource type.
        /// </summary>
        private Type getUtfprLookupFieldBindingSourceType(UtfprLookupFieldBase lookupField) {
            Type utfprLookupFieldBindingSourceType = null;
            try {
                if (lookupField != null) {
                    if (lookupField.DataSource != null) {
                        Type utfprLookupFieldDataSourceType = lookupField.DataSource.GetType();

                        if (utfprLookupFieldDataSourceType == typeof(BindingSource)) {
                            utfprLookupFieldBindingSourceType = (Type)((BindingSource)lookupField.DataSource).DataSource;
                        }
                    }
                }
            } catch { }
            return utfprLookupFieldBindingSourceType;
        }

        /// <summary>
        /// Get the DataSource type defined in the UtfprFormQueryBase property, BindingSource.
        /// </summary>
        private Type getFormQueryBindingSourceType(Type type) {
            Type formQueryBindingSourceType = null;
            try {
                if (typeof(UtfprFormQueryBase).IsAssignableFrom(type)) {
                    if (type.GetConstructor(new Type[] { typeof(ObjectContext) }) != null) {
                        PropertyInfo bindingSourceInfo = typeof(UtfprFormQueryBase).GetProperty(UtfprWindowsFormsConsts.UtfprFormQueryBase_BindingSource_PropertyName, BindingFlags.Public | BindingFlags.Instance);
                        if (bindingSourceInfo != null) {
                            object value = bindingSourceInfo.GetValue(Activator.CreateInstance(type, new object[] { null }), null);
                            if (value != null) {
                                if (value.GetType() == typeof(BindingSource)) {
                                    formQueryBindingSourceType = (Type)((BindingSource)value).DataSource;
                                }
                            }
                        }
                    }
                }
            } catch { }
            return formQueryBindingSourceType;
        }
        
        private void longOperation(object sender, DoWorkEventArgs e) {
            Type utfprLookupFieldBindingSourceType = getUtfprLookupFieldBindingSourceType(getUtfprLookupField());

            _componentList = new List<UtfprComponentDescription>();
            foreach (Type type in getDesiredTypes(utfprLookupFieldBindingSourceType, getAssemblyTypes())) {
                _componentList.Add(new UtfprComponentDescription(type));
            }
        }

        #endregion
    }
}

